// 创建路由信息
function createRoute(record, location) {
  const matched = [];

  // 将每一条路由对应的父子关系维护在栈中，【home，home/a】
  if (record) {
    while (record) {
      // 往头部添加记录，同时向上继续查找父亲，直到根
      matched.unshift(record);
      record = record.parent;
    }
  }

  return {
    ...location,
    matched
  }
}

// 执行队列钩子函数
function runQueue(queue, from, to, cb) {
  function next(index) {
    // 当所有的beforeEach 钩子函数执行完，再执行回调跳转路径更新视图等
    if (index >= queue.length) return cb();

    const hook = queue[index];
    // 执行beforeEach 传入form，to，next。
    // 在beforeEach 回调里面next() 执行完才会执行下一个beforeEach，直到队列任务清空，执行页面渲染逻辑
    hook(from, to, () => next(index + 1));
  }

  // 执行队列任务
  next(0);
}

class History {
  constructor(router) {
    this.router = router;
    this.current = createRoute(null, {
      path: "/"
    });
  }

  transitionTo(location, listener) {
    // 获取到对应的路由组件
    const record = this.router.match(location);
    // 当前路径对应的路由
    const route = createRoute(record, { path: location });

    // 当前跳转的路径和之前存的一样，而且匹配的结果也一样，则不需要重新跳转了
    if (location === this.current.path && this.current.matched.length === route.matched.length) {
      return
    }

    const queue = [].concat(this.router.beforeEachHooks);

    // 依次执行beforeEach 钩子函数
    runQueue(queue, this.current, route, () => {
      // 每次更新的是current，当current 改变的时候需要切换页面
      this.current = route;

      // 如果当路由切换的时候，也需要调用transitionTo 再次拿到新的记录，并执行回调修改路由地址
      listener && listener();

      // 更新当前的路由信息，即this._route
      this.cb && this.cb(route);
    })
  }

  // 路由变化时，保证将最新的路由记录传入回调进行更新
  listen(cb) {
    this.cb = cb;
  }
}

export default History
